home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / vol_300 / 329_01 / paste.c < prev    next >
C/C++ Source or Header  |  1988-12-14  |  14KB  |  578 lines

  1. /*-
  2.  * paste - a recreation of the Unix(Tm) paste(1) command.
  3.  *
  4.  * syntax:    paste file1 file2 ...
  5.  *        paste -dLIST file1 file2 ...
  6.  *        paste -s [-dLIST] file1 file2 ...
  7.  *
  8.  *    Copyright (C) 1984 by David M. Ihnat
  9.  *
  10.  * This program is a total rewrite of the Bell Laboratories Unix(Tm)
  11.  * command of the same name, as of System V.  It contains no proprietary
  12.  * code, and therefore may be used without violation of any proprietary
  13.  * agreements whatsoever.  However, you will notice that the program is
  14.  * copyrighted by me.  This is to assure the program does *not* fall
  15.  * into the public domain.  Thus, I may specify just what I am now:
  16.  * This program may be freely copied and distributed, provided this notice
  17.  * remains; it may not be sold for profit without express written consent of
  18.  * the author.
  19.  * Please note that I recreated the behavior of the Unix(Tm) 'paste' command
  20.  * as faithfully as possible, with minor exceptions (noted below); however,
  21.  * I haven't run a full set of regression * tests.  Thus, the user of
  22.  * this program accepts full responsibility for any effects or loss;
  23.  * in particular, the author is not responsible for any losses,
  24.  * explicit or incidental, that may be incurred through use of this program.
  25.  *
  26.  * The changes to the program, with one exception, are transparent to
  27.  * a user familiar with the Unix command of the same name.  These changes
  28.  * are:
  29.  *
  30.  * 1) The '-s' option had a bug in the Unix version when used with multiple
  31.  *    files.  (It would repeat each file in a list, i.e., for
  32.  *    'paste -s file1 file2 file3', it would list
  33.  *    <file1\n><file1\n><file2\n><file1\n><file2\n><file3\n>
  34.  *    I fixed this, and reported the bug to the providers of the command in
  35.  *    Unix.
  36.  *
  37.  * 2) The list of valid escape sequences has been expanded to include
  38.  *    \b,\f,\r, and \v.  (Just because *I* can't imagine why you'd want
  39.  *    to use them doesn't mean I should keep them from you.)
  40.  *
  41.  * 3) There is no longer any restriction on line length.
  42.  *
  43.  * I ask that any bugs (and, if possible, fixes) be reported to me when
  44.  * possible.  -David Ihnat (312) 784-4544 ihuxx!ignatz
  45. **
  46. **    Roberto Artigas Jr
  47. **    P.O. Box 281415
  48. **    Memphis, TN 38168-1415
  49. **    work: 901-762-6092
  50. **    home: 901-373-4738
  51. **
  52. **    1988.12.08 - Changes to get this to work under OS/2
  53. **        Used C/2 version 1.10 under OS/2 E 1.1
  54. **        Commands where:
  55. **        cl -c -AL paste.c
  56. **        link /st:4000 paste,/noi,paste,llibce+os2, ;
  57. **
  58. **    Worked out includes
  59. **    Fixed command line processing problem
  60. **    Fixed delimiter string definition problem
  61. **    Added delimiter string size checking
  62. **
  63. **+
  64. */
  65. #define    CPM    0
  66. #define    DOS    0
  67. #define    OS2    1
  68.  
  69. #include <stdio.h>
  70.  
  71. #if    CPM
  72. extern int errno;
  73. #endif
  74.  
  75. #if    CPM
  76. #define    _MAXSZ        512
  77. #else
  78. #define    _MAXSZ        (BUFSIZ*4)
  79. #endif
  80.  
  81. #if    DOS || OS2
  82. #include    <ctype.h>
  83. #include    <stdlib.h>
  84. #include    <string.h>
  85. #endif
  86.  
  87. /* I'd love to use enums, but not everyone has them.  Portability, y'know. */
  88. #define NODELIM        1
  89. #define USAGE        2
  90. #define BADFILE        3
  91. #define TOOMANY        4
  92. #define    DELIMTOOLONG    5
  93.  
  94. #define    TAB        '\t'
  95. #define    NL        '\n'
  96. #define    BS        '\b'
  97. #define    FF        '\f'
  98. #define    CR        '\r'
  99. #define    VT        '\v'
  100. #define    DEL        '\177'
  101.  
  102. #define _MAXFILES    12
  103. #define    CLOSED        ((FILE *)-1)
  104. #define ENDLIST        ((FILE *)-2)
  105. #define COMMAND        "paste"
  106. #define    SZDELIM        31
  107.  
  108. char *cmdnam;
  109.  
  110. short int dflag,
  111.  sflag;
  112. char delims[SZDELIM] = {TAB, '\0'};    /* Delimiter STRING!!!! */
  113.  
  114. /*
  115.  * Function prototypes 
  116.  */
  117. void prerr();
  118. void delimbuild();
  119. void docol();
  120. void doserial();
  121.  
  122.  
  123. /*-
  124. **    name:        main
  125. **    purpose:    Begin program here
  126. **+
  127. */
  128. int 
  129. main(argc, argv)
  130. int argc;
  131. char *argv[];
  132. {
  133.     char *strcpy();
  134.  
  135.     dflag = sflag = 0;
  136.  
  137. #if    CPM
  138.     cmdnam = COMMAND;
  139. #else
  140.     cmdnam = *argv;
  141. #endif
  142.  
  143.     /* Skip invocation name */
  144.     argv++;
  145.     argc--;
  146.  
  147.     if (!argc)
  148.     prerr(USAGE, "");
  149.  
  150.     /* First, parse input options */
  151.  
  152.     while (argv[0] && argv[0][0] == '-' && argv[0][1] != '\0')
  153.     {
  154.     switch (argv[0][1])
  155.     {
  156.     case 'd':
  157.     case 'D':
  158.         /* Delimiter character(s) */
  159.         if (strlen(&argv[0][2]) > SZDELIM)
  160.         prerr(DELIMTOOLONG,
  161.               (char *) &argv[0][2]);
  162.         if (*delims == '\0')
  163.         prerr(NODELIM, "");
  164.         else
  165.         {
  166.         strcpy(delims, &argv[0][2]);
  167.         delimbuild(delims);
  168.         }
  169.  
  170.         break;
  171.  
  172.     case 's':
  173.     case 'S':
  174.         sflag++;
  175.         break;
  176.  
  177.     default:
  178.         prerr(USAGE, "");
  179.     }
  180.     argv++;
  181.     argc--;
  182.     }
  183.  
  184.     /*
  185.      * If no files specified, simply exit.  Otherwise, if not the old '-s'
  186.      * option, process all files. If '-s', then process files one-at-a-time. 
  187.      */
  188.     if (!sflag)
  189.     docol(argc, argv);           /* Column paste */
  190.     else
  191.     doserial(argc, argv);           /* Serial paste */
  192.  
  193.     return (0);
  194. }
  195.  
  196. void 
  197. docol(nfiles, fnamptr)
  198. int nfiles;
  199. char **fnamptr;
  200. {
  201.     char iobuff[_MAXSZ];           /* i/o buffer for the fgets */
  202.     short int somedone;               /* flag for blank field handling */
  203.  
  204.     /*
  205.      * There is a strange case where all files are just ready to be closed,
  206.      * or will on this round.  In that case, the string of delimiters must
  207.      * be preserved.  delbuf[1] ->delbuf[MAXFILES+1] provides intermediate
  208.      * storage for closed files, if needed; delbuf[0] is the current index. 
  209.      */
  210.     char delbuf[_MAXFILES + 2];
  211.  
  212.     FILE *fileptr[_MAXFILES + 1];
  213.     FILE *fopen();
  214.  
  215.     char *fgets();
  216.  
  217.     int filecnt;               /* Set to number of files to process */
  218.     register char *delimptr;           /* Cycling delimiter pointer */
  219.     int index;                   /* Working variable */
  220.     int strend;                   /* End of string in buffer */
  221.  
  222.     /*
  223.      * Perform column paste.  First, attempt to open all files. (This could
  224.      * be expanded to an infinite number of files, but at the (considerable)
  225.      * expense of remembering the file and its current offset, then
  226.      * opening/reading/closing.  The commands' utility doesn't warrant the
  227.      * effort; at least, to me...) 
  228.      */
  229.  
  230.     for (filecnt = 0; (nfiles > 0) && (filecnt < _MAXFILES); filecnt++, nfiles--, fnamptr++)
  231.     {
  232.     if (fnamptr[0][0] == '-')
  233.         fileptr[filecnt] = stdin;
  234.     else
  235.     {
  236. #if    DOS || OS2
  237.         fileptr[filecnt] = fopen(*fnamptr, "rb");
  238. #else
  239.         fileptr[filecnt] = fopen(*fnamptr, "r");
  240. #endif
  241.         if (fileptr[filecnt] == NULL)
  242.         prerr(BADFILE, *fnamptr);
  243.     }
  244.     }
  245.  
  246.     fileptr[filecnt] = ENDLIST;           /* End of list. */
  247.  
  248.     if (nfiles)
  249.     prerr(TOOMANY, "");
  250.  
  251.     /*
  252.      * Have all files.  Now, read a line from each file, and output to
  253.      * stdout.  Notice that the old 511 character limitation on the line
  254.      * length no longer applies, since this program doesn't do the
  255.      * buffering.  Do this until you go through the loop and don't
  256.      * successfully read from any of the files. 
  257.      */
  258.     for (; filecnt;)
  259.     {
  260.     somedone = 0;               /* Blank field handling flag */
  261.     delimptr = delims;           /* Start at beginning of delim list */
  262.     delbuf[0] = 0;               /* No squirreled delims */
  263.  
  264.     for (index = 0; (fileptr[index] != ENDLIST) && filecnt; index++)
  265.     {
  266.         /*
  267.          * Read a line and immediately output. If it's too big for the
  268.          * buffer, then dump what was read and go back for more. 
  269.          *
  270.          * Otherwise, if it is from the last file, then leave the carriage
  271.          * return in place; if not, replace with a delimiter (if any) 
  272.          */
  273.  
  274.         strend = 0;               /* Set so can easily detect EOF */
  275.  
  276.         if (fileptr[index] != CLOSED)
  277.  
  278.         while (fgets(iobuff, (_MAXSZ - 1), fileptr[index]) != (char *) NULL)
  279.         {
  280.             strend = strlen(iobuff);    /* Did the buffer fill? */
  281.  
  282.             if (strend == (_MAXSZ - 1))
  283.             {
  284.             /* Gosh, what a long line. */
  285.             fputs(iobuff, stdout);
  286.             strend = 0;
  287.             continue;
  288.             }
  289.             /* Ok got whole line in buffer. */
  290.             break;           /* Out of loop for this file */
  291.         }
  292.  
  293.         /*
  294.          * Ended either on an EOF (well, actually NULL return-- it
  295.          * *could* be some sort of file error, but but if the file was
  296.          * opened successfully, this is unlikely. Besides, error
  297.          * checking on streams doesn't allow us t